<!doctype html><html class="use-motion" lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="0axhmy0-lA3xPbR8wPgyWlMZjwWSCfagKukdyeCQ7LU"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css"><meta name="keywords" content="reconnect,重连,vhostuser,QEMU,OpenvSwitch,SDN,Network"><link rel="alternate" href="/atom.xml" title="赵占旭的博客" type="application/atom+xml"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0"><meta name="description" content="我们这次想要解决的问题是ovs-dpdk的vhost-user和qemu虚拟机重连的问题。"><meta name="keywords" content="reconnect,重连,vhostuser,QEMU,OpenvSwitch,SDN,Network"><meta property="og:type" content="article"><meta property="og:title" content="QEMU vhostuser重连"><meta property="og:url" content="http://zhaozhanxu.com/2017/02/16/QEMU/2017-02-16-qemu-reconnect/index.html"><meta property="og:site_name" content="赵占旭的博客"><meta property="og:description" content="我们这次想要解决的问题是ovs-dpdk的vhost-user和qemu虚拟机重连的问题。"><meta property="og:locale" content="en"><meta property="og:updated_time" content="2017-12-11T14:31:18.941Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="QEMU vhostuser重连"><meta name="twitter:description" content="我们这次想要解决的问题是ovs-dpdk的vhost-user和qemu虚拟机重连的问题。"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",sidebar:{position:"left",display:"post",offset:342,offset_float:0},fancybox:!0,motion:!0}</script><link rel="canonical" href="http://zhaozhanxu.com/2017/02/16/QEMU/2017-02-16-qemu-reconnect/"><title> QEMU vhostuser重连 | 赵占旭的博客</title></head><body itemscope itemtype="http://schema.org/WebPage" lang="en"><script>!function(e,a,t,c,n,g,o){e.GoogleAnalyticsObject="ga",e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,g=a.createElement("script"),o=a.getElementsByTagName("script")[0],g.async=1,g.src="//www.google-analytics.com/analytics.js",o.parentNode.insertBefore(g,o)}(window,document),ga("create","3a235f2280210fdd34cebc8c3647e5b8","auto"),ga("send","pageview")</script><script type="text/javascript">var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="//hm.baidu.com/hm.js?UA-90327054-1";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script type="text/javascript">!function(){var e=document.createElement("script");e.src="//tajs.qq.com/stats?sId=60806179";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><div class="container one-collumn sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader" style="background-image:url(/images/public/home_bg.jpg)"><div class="header-cover"><div class="header-inner"><a class="site-home" href="/">赵占旭的博客</a><div class="site-meta"><div class="custom-logo-site-title"><div href="/" class="brand"><span class="logo-line-before"><i></i></span> <span class="site-title">赵占旭的博客</span><span class="logo-line-after"><i></i></span></div></div><h1 class="site-subtitle" itemprop="description">一花一世界 一叶一菩提</h1></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-categories"><a href="/categories" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br> Categories</a></li><li class="menu-item menu-item-tags"><a href="/tags" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br> Tags</a></li><li class="menu-item menu-item-archives"><a href="/archives" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> Archives</a></li><li class="menu-item menu-item-about"><a href="/about" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i><br> About</a></li><li class="menu-item menu-item-search"><a href="javascript:" ; class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> Search</a></li></ul><div class="site-search"><div class="popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i></span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"> <input autocapitalize="off" autocomplete="off" autocorrect="off" placeholder="search.placeholder" spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div><div class="header-post"><div class="post-header"><div class="tags"> <a href="/tags/Network/" rel="tag" title="Network">Network</a> <a href="/tags/SDN/" rel="tag" title="SDN">SDN</a> <a href="/tags/QEMU/" rel="tag" title="QEMU">QEMU</a> <a href="/tags/OpenvSwitch/" rel="tag" title="OpenvSwitch">OpenvSwitch</a></div><h1>QEMU vhostuser重连</h1><h2 class="subtitle">给出了重连的方案，其实没做啥</h2><div class="post-time"> <span class="post-meta-item-text">Posted on</span> <time itemprop="dateCreated" datetime="2017-02-16T00:00:00+08:00" content="2017-02-16" title="2017-02-16 00:00:00">2017-02-16</time></div></div></div></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><link itemprop="mainEntityOfPage" href="http://zhaozhanxu.com/2017/02/16/QEMU/2017-02-16-qemu-reconnect/"><span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="赵占旭"><meta itemprop="description" content=""><meta itemprop="image" content="/images/public/me.jpg"></span><span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="赵占旭的博客"> <span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject"><img style="display:none" itemprop="url image" alt="赵占旭的博客" src=""></span></span><div class="post-inner"><header class="post-header"><h2 class="post-title" itemprop="name headline"> QEMU vhostuser重连</h2><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <span class="post-meta-item-text">Posted on</span> <time title="Post created" itemprop="dateCreated datePublished" datetime="2017-02-16T00:00:00+08:00">2017-02-16</time></span></div></header><div class="post-info"><div class="post-categories"> <a class="color" href="/categories/QEMU/" rel="tag">QEMU</a></div><div class="post-tags"> <a class="color" href="/tags/Network/" rel="tag">Network</a> <a class="color" href="/tags/SDN/" rel="tag">SDN</a> <a class="color" href="/tags/QEMU/" rel="tag">QEMU</a> <a class="color" href="/tags/OpenvSwitch/" rel="tag">OpenvSwitch</a></div><div class="clearfix"></div></div><div class="post-body" itemprop="articleBody"><p>我们这次想要解决的问题是ovs-dpdk的vhost-user和qemu虚拟机重连的问题。</p><a id="more"></a><h1 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h1><hr><p>qemu version: 2.7.1<br>ovs-dpdk重启后，qemu虚拟机网络不通。</p><h2 id="ovs为什么不存在这个问题"><a href="#ovs为什么不存在这个问题" class="headerlink" title="ovs为什么不存在这个问题"></a>ovs为什么不存在这个问题</h2><p>我们都知道ovs的vhost其实真正起作用的是<code>vhost_worker</code>线程，都是发包方去触发该线程，该线程再做报文处理，去调用ovs的kernel端的datapath，所以这个时候ovs如果挂掉，再恢复是不影响ovs处理报文的。</p><h2 id="ovs-dpdk问题原因"><a href="#ovs-dpdk问题原因" class="headerlink" title="ovs-dpdk问题原因"></a>ovs-dpdk问题原因</h2><p>当使用ovs-dpdk的时候，和qemu虚拟机连接使用的是vhost-user，采用的是unix socket domain的连接收发报文，默认是ovs-dpdk作为server端，而qemu作为client端，这个时候ovs-dpdk如果崩溃掉，那么连接就会出问题，ovs-dpdk即使恢复也不能和虚拟机正常通信了。</p><h1 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h1><hr><p>下面先说考虑的几个方案：</p><ul><li>让qemu做server端，ovs-dpdk做client端，重连由ovs-dpdk来做，这个已经验证可以实施，只是需要注意socket文件需要指定，默认是空的。经过验证，qemu2.7和2.3都支持。</li><li>server和client保持不变，但是需要qemu默认支持reconnect，目前的情况是qemu默认是关闭重连的，需要修改代码，修改量比较小。</li><li>server和client保持不变，但是需要qemu支持设置reconnect，而libvirt也需要xml支持设置reconnect，这个方案不进需要qemu修改部分代码，而且libvirt源码也要进行修改。</li></ul><h1 id="方案的优劣"><a href="#方案的优劣" class="headerlink" title="方案的优劣"></a>方案的优劣</h1><hr><p>Thinking …</p><h1 id="qemu相关源码"><a href="#qemu相关源码" class="headerlink" title="qemu相关源码"></a>qemu相关源码</h1><hr><p>main函数位于vl.c文件中，因为我们主要关注点在qemu连接ovs的vhost-user，所以main函数我们只看相关的。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv, <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (qemu_opts_foreach(qemu_find_opts(<span class="string">"chardev"</span>),</span><br><span class="line">                    chardev_init_func, <span class="literal">NULL</span>, <span class="literal">NULL</span>)) &#123;</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (net_init_clients() &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    main_loop();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>chardev_init_func--&gt;qemu_chr_new_from_opts</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">CharDriverState *<span class="title">qemu_chr_new_from_opts</span><span class="params">(QemuOpts *opts,</span></span></span><br><span class="line">            void (*init)(struct CharDriverState *s),</span><br><span class="line">            Error **errp)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (cd-&gt;parse) &#123;</span><br><span class="line">        <span class="comment">//调用qemu_chr_parse_socket</span></span><br><span class="line">        <span class="comment">//主要是解析vhost-user的一些参数</span></span><br><span class="line">        cd-&gt;parse(opts, backend, &amp;local_err);</span><br><span class="line">        <span class="keyword">if</span> (local_err) &#123;</span><br><span class="line">            error_propagate(errp, local_err);</span><br><span class="line">            <span class="keyword">goto</span> qapi_out;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建vhost-user连接</span></span><br><span class="line">    ret = qmp_chardev_add(bid ? bid : id, backend, errp);</span><br><span class="line">    <span class="keyword">if</span> (!ret) &#123;</span><br><span class="line">        <span class="keyword">goto</span> qapi_out;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ChardevReturn *<span class="title">qmp_chardev_add</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *id, ChardevBackend *backend,</span></span></span><br><span class="line"><span class="function"><span class="params">            Error **errp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (i = backends; i; i = i-&gt;next) &#123;</span><br><span class="line">        cd = i-&gt;data;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (cd-&gt;kind == backend-&gt;type) &#123;</span><br><span class="line">            <span class="comment">//调用qmp_chardev_open_socket创建连接</span></span><br><span class="line">            chr = cd-&gt;create(id, backend, ret, &amp;local_err);</span><br><span class="line">            <span class="keyword">if</span> (local_err) &#123;</span><br><span class="line">                error_propagate(errp, local_err);</span><br><span class="line">                <span class="keyword">goto</span> out_error;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">qemu_chr_parse_socket</span><span class="params">(QemuOpts *opts, ChardevBackend *backend,</span></span></span><br><span class="line"><span class="function"><span class="params">            Error **errp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//检测是否将qemu作为server端，默认是不支持</span></span><br><span class="line">    <span class="keyword">bool</span> is_listen      = qemu_opt_get_bool(opts, <span class="string">"server"</span>, <span class="literal">false</span>);</span><br><span class="line">    <span class="comment">//默认是wait</span></span><br><span class="line">    <span class="keyword">bool</span> is_waitconnect = is_listen &amp;&amp; qemu_opt_get_bool(opts, <span class="string">"wait"</span>, <span class="literal">true</span>);</span><br><span class="line">    <span class="comment">//判断是否支持reconnect，默认不支持，简单修改的话此处修改0即可</span></span><br><span class="line">    <span class="keyword">int64_t</span> reconnect   = qemu_opt_get_number(opts, <span class="string">"reconnect"</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">//unix socket domain path</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span> *path = qemu_opt_get(opts, <span class="string">"path"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//下面的函数会用到</span></span><br><span class="line">    sock-&gt;has_server = <span class="literal">true</span>;</span><br><span class="line">    sock-&gt;server = is_listen;</span><br><span class="line">    sock-&gt;has_wait = <span class="literal">true</span>;</span><br><span class="line">    sock-&gt;wait = is_waitconnect;</span><br><span class="line">    sock-&gt;has_reconnect = <span class="literal">true</span>;</span><br><span class="line">    sock-&gt;reconnect = reconnect;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> CharDriverState *<span class="title">qmp_chardev_open_socket</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *id,</span></span></span><br><span class="line"><span class="function"><span class="params">            ChardevBackend *backend,</span></span></span><br><span class="line"><span class="function"><span class="params">            ChardevReturn *ret,</span></span></span><br><span class="line"><span class="function"><span class="params">            Error **errp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//鉴于之前函数写死了这几项为true，所以这几块都是看上面的解析</span></span><br><span class="line">    <span class="keyword">bool</span> is_listen      = sock-&gt;has_server  ? sock-&gt;server  : <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">bool</span> is_waitconnect = sock-&gt;has_wait    ? sock-&gt;wait    : <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">int64_t</span> reconnect   = sock-&gt;has_reconnect ? sock-&gt;reconnect : <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    s-&gt;is_listen = is_listen;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将一些函数注册</span></span><br><span class="line">    chr-&gt;opaque = s;</span><br><span class="line">    chr-&gt;chr_wait_connected = tcp_chr_wait_connected;</span><br><span class="line">    chr-&gt;chr_write = tcp_chr_write;</span><br><span class="line">    chr-&gt;chr_sync_read = tcp_chr_sync_read;</span><br><span class="line">    chr-&gt;chr_close = tcp_chr_close;</span><br><span class="line">    chr-&gt;chr_disconnect = tcp_chr_disconnect;</span><br><span class="line">    chr-&gt;get_msgfds = tcp_get_msgfds;</span><br><span class="line">    chr-&gt;set_msgfds = tcp_set_msgfds;</span><br><span class="line">    chr-&gt;chr_add_client = tcp_chr_add_client;</span><br><span class="line">    chr-&gt;chr_add_watch = tcp_chr_add_watch;</span><br><span class="line">    chr-&gt;chr_update_read_handler = tcp_chr_update_read_handler;</span><br><span class="line">    <span class="comment">/* be isn't opened until we get a connection */</span></span><br><span class="line">    chr-&gt;explicit_be_open = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//如果是server模式即不操作，client模式判定是否设置了reconnect，</span></span><br><span class="line">    <span class="comment">//如果设置了则赋值</span></span><br><span class="line">    <span class="keyword">if</span> (is_listen) &#123;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (reconnect &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        s-&gt;reconnect_time = reconnect;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//支持reconnect，需要做一些事情</span></span><br><span class="line">    <span class="keyword">if</span> (s-&gt;reconnect_time) &#123;</span><br><span class="line">        sioc = qio_channel_socket_new();</span><br><span class="line">        <span class="comment">//创建一个线程，运行一个routine，运行connect连接server的工作，</span></span><br><span class="line">        qio_channel_socket_connect_async(sioc, s-&gt;addr,</span><br><span class="line">                    qemu_chr_socket_connected,</span><br><span class="line">                    chr, <span class="literal">NULL</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//不支持reconnect的时候，如果是server端，则进行如下操作</span></span><br><span class="line">        <span class="keyword">if</span> (s-&gt;is_listen) &#123;</span><br><span class="line">            sioc = qio_channel_socket_new();</span><br><span class="line">            <span class="comment">//调用listen</span></span><br><span class="line">            <span class="keyword">if</span> (qio_channel_socket_listen_sync(sioc, s-&gt;addr, errp) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">goto</span> error;</span><br><span class="line">            &#125;</span><br><span class="line">            s-&gt;listen_ioc = sioc;</span><br><span class="line">            <span class="comment">//从上面可以看到调用的是tcp_chr_wait_connected，</span></span><br><span class="line">            <span class="comment">//此处是server端，调用accept，如果是client，调用connect</span></span><br><span class="line">            <span class="keyword">if</span> (is_waitconnect &amp;&amp;</span><br><span class="line">                        qemu_chr_wait_connected(chr, errp) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">goto</span> error;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!s-&gt;ioc) &#123;</span><br><span class="line">                s-&gt;listen_tag = qio_channel_add_watch(</span><br><span class="line">                            QIO_CHANNEL(s-&gt;listen_ioc), G_IO_IN,</span><br><span class="line">                            tcp_chr_accept, chr, <span class="literal">NULL</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//此处和上面相反，是client，所以调用connect</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (qemu_chr_wait_connected(chr, errp) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">goto</span> error;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> chr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>net_init_clients--&gt;net_init_netdev--&gt;net_client_init--&gt;net_client_init1--&gt;net_client_init_fun[]--&gt;net_init_vhost_user</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">net_init_vhost_user</span><span class="params">(<span class="keyword">const</span> Netdev *netdev, <span class="keyword">const</span> <span class="keyword">char</span> *name,</span></span></span><br><span class="line"><span class="function"><span class="params">            NetClientState *peer, Error **errp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//解析vhost-user的信息</span></span><br><span class="line">    chr = net_vhost_parse_chardev(vhost_user_opts, errp);</span><br><span class="line">    <span class="keyword">if</span> (!chr) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> net_vhost_user_init(peer, <span class="string">"vhost_user"</span>, name, chr, queues);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>net_vhost_parse_chardev--&gt;net_vhost_chardev_opts</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">net_vhost_chardev_opts</span><span class="params">(<span class="keyword">void</span> *opaque,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> <span class="keyword">char</span> *name, <span class="keyword">const</span> <span class="keyword">char</span> *value,</span></span></span><br><span class="line"><span class="function"><span class="params">            Error **errp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//此处没有什么要说的，主要是不支持reconnect，如果要支持需要添加一行代码</span></span><br><span class="line">    <span class="comment">//只要类似server的行，并不需要做什么操作，前面已经做了，</span></span><br><span class="line">    <span class="comment">//这里只是不写的话会进else报错</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(name, <span class="string">"backend"</span>) == <span class="number">0</span> &amp;&amp; <span class="built_in">strcmp</span>(value, <span class="string">"socket"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        props-&gt;is_socket = <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(name, <span class="string">"path"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        props-&gt;is_unix = <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(name, <span class="string">"server"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        error_setg(errp,</span><br><span class="line">                    <span class="string">"vhost-user does not support a chardev with option %s=%s"</span>,</span><br><span class="line">                    name, value);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">net_vhost_user_init</span><span class="params">(NetClientState *peer, <span class="keyword">const</span> <span class="keyword">char</span> *device,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> <span class="keyword">char</span> *name, CharDriverState *chr,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> queues)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    NetClientState *nc, *nc0 = <span class="literal">NULL</span>;</span><br><span class="line">    VhostUserState *s;</span><br><span class="line">    <span class="keyword">int</span> i;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; queues; i++) &#123;</span><br><span class="line">        <span class="comment">//初始化队列</span></span><br><span class="line">        nc = qemu_new_net_client(&amp;net_vhost_user_info, peer, device, name);</span><br><span class="line">        <span class="keyword">if</span> (!nc0) &#123;</span><br><span class="line">            nc0 = nc;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">snprintf</span>(nc-&gt;info_str, <span class="keyword">sizeof</span>(nc-&gt;info_str), <span class="string">"vhost-user%d to %s"</span>,</span><br><span class="line">                    i, chr-&gt;label);</span><br><span class="line"></span><br><span class="line">        nc-&gt;queue_index = i;</span><br><span class="line"></span><br><span class="line">        s = DO_UPCAST(VhostUserState, nc, nc);</span><br><span class="line">        s-&gt;chr = chr;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    s = DO_UPCAST(VhostUserState, nc, nc0);</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        Error *err = <span class="literal">NULL</span>;</span><br><span class="line">        <span class="comment">//当开启reconnect的时候这里才会继续调用,这块需要再看看</span></span><br><span class="line">        <span class="keyword">if</span> (qemu_chr_wait_connected(chr, &amp;err) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            error_report_err(err);</span><br><span class="line">            <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//添加event事件,这块最终会添加一个端口hung up时的调用qio_channel_fd_source_dispatch</span></span><br><span class="line">        <span class="comment">//如果前面设置了reonnect的时候，后面最终会调用到qemu_chr_socket_restart_timer最终重连</span></span><br><span class="line">        qemu_chr_add_handlers(chr, <span class="literal">NULL</span>, <span class="literal">NULL</span>,</span><br><span class="line">                    net_vhost_user_event, nc0-&gt;name);</span><br><span class="line">    &#125; <span class="keyword">while</span> (!s-&gt;started);</span><br><span class="line"></span><br><span class="line">    assert(s-&gt;vhost_net);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以下就是端口hung up的时候的调用流程：</p><p><code>main_loop--&gt;main_loop_wait--&gt;os_host_main_loop_wait--&gt;glib_pollfds_poll--&gt;g_main_context_dispatch--&gt;qio_channel_fd_source_dispatch--&gt;net_vhost_user_watch--&gt;qemu_chr_disconnect--&gt;tcp_chr_disconnect</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">tcp_chr_disconnect</span><span class="params">(CharDriverState *chr)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//首先调用closed关闭、清除一些信息</span></span><br><span class="line">    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);</span><br><span class="line">    <span class="keyword">if</span> (s-&gt;reconnect_time) &#123;</span><br><span class="line">        <span class="comment">//当设置了重连之后，会调用该接口进行重连</span></span><br><span class="line">        qemu_chr_socket_restart_timer(chr);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>qemu_chr_be_event--&gt;net_vhost_user_event</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">net_vhost_user_event</span><span class="params">(<span class="keyword">void</span> *opaque, <span class="keyword">int</span> event)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (event) &#123;</span><br><span class="line">        <span class="comment">//创建设备信息，一开始和后面重连之后会调用</span></span><br><span class="line">        <span class="keyword">case</span> CHR_EVENT_OPENED:</span><br><span class="line">            s-&gt;watch = qemu_chr_fe_add_watch(s-&gt;chr, G_IO_HUP,</span><br><span class="line">                        net_vhost_user_watch, s);</span><br><span class="line">            <span class="keyword">if</span> (vhost_user_start(queues, ncs) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                qemu_chr_disconnect(s-&gt;chr);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            qmp_set_link(name, <span class="literal">true</span>, &amp;err);</span><br><span class="line">            s-&gt;started = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">            <span class="comment">//设备hung up之后会调用</span></span><br><span class="line">        <span class="keyword">case</span> CHR_EVENT_CLOSED:</span><br><span class="line">            qmp_set_link(name, <span class="literal">false</span>, &amp;err);</span><br><span class="line">            vhost_user_stop(queues, ncs);</span><br><span class="line">            g_source_remove(s-&gt;watch);</span><br><span class="line">            s-&gt;watch = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面的流程是当上面重连了之后，会在空闲的时候调用<code>net_vhost_user_event</code>的<code>CHR_EVENT_OPENED</code>，最终可以实现通信了。</p><p><code>main_loop--&gt;main_loop_wait--&gt;os_host_main_loop_wait--&gt;glib_pollfds_poll--&gt;g_main_context_dispatch--&gt;gio_task_thread_result--&gt;qio_task_complete--&gt;qemu_chr_socket_connected--&gt;tcp_chr_new_client--&gt;tcp_chr_connect--&gt;qemu_chr_be_generic_open--&gt;qemu_chr_be_event--&gt;net_vhost_user_event</code></p><h1 id="libvirt相关源码"><a href="#libvirt相关源码" class="headerlink" title="libvirt相关源码"></a>libvirt相关源码</h1><hr><p>我们目前只关心<code>virsh define xxx.xml</code>命令相关的一些代码。因为这里会解析xml文件，目前只支持server和client两种模式，并不支持reconnect。对应的接口为<code>cmdDefine</code>。</p><p><code>cmdDefine--&gt;virDomainDefineXML--&gt;conn-&gt;driver-&gt;domainDefineXML--&gt;qemuDomainDefineXML--&gt;qemuDomainDefineXMLFlags--&gt;virDomainDefParseString--&gt;virDomainDefParse--&gt;virDomainDefParseNode--&gt;virDomainDefParseXML--&gt;virDomainNetDefParseXML</code></p><p><code>virDomainNetDefParseXML</code>主要负责网络模块的解析，我们只关注的是vhostuser部分。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> virDomainNetDefPtr</span><br><span class="line">virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,</span><br><span class="line">            xmlNodePtr node,</span><br><span class="line">            xmlXPathContextPtr ctxt,</span><br><span class="line">            virHashTablePtr bootHash,</span><br><span class="line">            <span class="keyword">char</span> *prefix,</span><br><span class="line">            <span class="keyword">unsigned</span> <span class="keyword">int</span> flags)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">while</span> (cur != <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (cur-&gt;type == XML_ELEMENT_NODE) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!vhostuser_path &amp;&amp; !vhostuser_mode &amp;&amp; !vhostuser_type</span><br><span class="line">                        &amp;&amp; def-&gt;type == VIR_DOMAIN_NET_TYPE_VHOSTUSER &amp;&amp;</span><br><span class="line">                        xmlStrEqual(cur-&gt;name, BAD_CAST <span class="string">"source"</span>)) &#123;</span><br><span class="line">                <span class="comment">//获取type path mode等信息</span></span><br><span class="line">                vhostuser_type = virXMLPropString(cur, <span class="string">"type"</span>);</span><br><span class="line">                vhostuser_path = virXMLPropString(cur, <span class="string">"path"</span>);</span><br><span class="line">                vhostuser_mode = virXMLPropString(cur, <span class="string">"mode"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">switch</span> (def-&gt;type) &#123;</span><br><span class="line">        <span class="keyword">case</span> VIR_DOMAIN_NET_TYPE_VHOSTUSER:</span><br><span class="line">            def-&gt;data.vhostuser-&gt;type = VIR_DOMAIN_CHR_TYPE_UNIX;</span><br><span class="line">            def-&gt;data.vhostuser-&gt;data.nix.path = vhostuser_path;</span><br><span class="line">            vhostuser_path = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//判断是server还是client模式，后面会用到</span></span><br><span class="line">            <span class="comment">//这里只支持server和client模式，其他的会报错</span></span><br><span class="line">            <span class="comment">//所以我们想要支持reconnect的话需要在这里进行修改</span></span><br><span class="line">            <span class="keyword">if</span> (STREQ(vhostuser_mode, <span class="string">"server"</span>)) &#123;</span><br><span class="line">                def-&gt;data.vhostuser-&gt;data.nix.listen = <span class="literal">true</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (STREQ(vhostuser_mode, <span class="string">"client"</span>)) &#123;</span><br><span class="line">                def-&gt;data.vhostuser-&gt;data.nix.listen = <span class="literal">false</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, <span class="string">"%s"</span>,</span><br><span class="line">                            _(<span class="string">"Wrong &lt;source&gt; 'mode' attribute "</span></span><br><span class="line">                                <span class="string">"specified with &lt;interface "</span></span><br><span class="line">                                <span class="string">"type='vhostuser'/&gt;"</span>));</span><br><span class="line">                <span class="keyword">goto</span> error;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>记下来看一下<code>virsh start xxx</code>是怎么将xml数据转换成qemu的命令行的，如果上面添加支持reconnect的话，这块的参数也要相应的添加。对应的接口为<code>cmdStart</code>。</p><p><code>cmdStart--&gt;virDomainCreate--&gt;conn-&gt;driver-&gt;domainCreate--&gt;qemuDomainCreate--&gt;qemuDomainCreateWithFlags--&gt;qemuDomainCreateWithFlags--&gt;qemuDomainObjStart--&gt;qemuProcessStart--&gt;qemuProcessLaunch--&gt;qemuBuildCommandLine--&gt;qemuBuildNetCommandLine--&gt;qemuBuildInterfaceCommandLine--&gt;qemuBuildVhostuserCommandLine</code></p><p><code>qemuBuildVhostuserCommandLine</code>主要是生成vhostuser相关的参数。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span></span><br><span class="line">qemuBuildVhostuserCommandLine(virQEMUDriverPtr driver,</span><br><span class="line">            virLogManagerPtr logManager,</span><br><span class="line">            virCommandPtr cmd,</span><br><span class="line">            virDomainDefPtr def,</span><br><span class="line">            virDomainNetDefPtr net,</span><br><span class="line">            virQEMUCapsPtr qemuCaps,</span><br><span class="line">            <span class="keyword">unsigned</span> <span class="keyword">int</span> bootindex)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">switch</span> ((virDomainChrType) net-&gt;data.vhostuser-&gt;type) &#123;</span><br><span class="line">        <span class="keyword">case</span> VIR_DOMAIN_CHR_TYPE_UNIX:</span><br><span class="line">            <span class="comment">//解析chardev部分</span></span><br><span class="line">            <span class="keyword">if</span> (!(chardev = qemuBuildChrChardevStr(logManager, cmd, cfg, def,</span><br><span class="line">                                net-&gt;data.vhostuser,</span><br><span class="line">                                net-&gt;info.alias, qemuCaps, <span class="literal">false</span>)))</span><br><span class="line">              <span class="keyword">goto</span> error;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//解析netdev部分</span></span><br><span class="line">    <span class="keyword">if</span> (!(netdev = qemuBuildHostNetStr(net, driver,</span><br><span class="line">                        <span class="string">','</span>, <span class="number">-1</span>,</span><br><span class="line">                        <span class="literal">NULL</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="number">0</span>)))</span><br><span class="line">      <span class="keyword">goto</span> error;</span><br><span class="line"></span><br><span class="line">    virCommandAddArg(cmd, <span class="string">"-chardev"</span>);</span><br><span class="line">    <span class="comment">//添加chardev参数</span></span><br><span class="line">    virCommandAddArg(cmd, chardev);</span><br><span class="line">    VIR_FREE(chardev);</span><br><span class="line"></span><br><span class="line">    virCommandAddArg(cmd, <span class="string">"-netdev"</span>);</span><br><span class="line">    <span class="comment">//添加netdev部分参数</span></span><br><span class="line">    virCommandAddArg(cmd, netdev);</span><br><span class="line">    VIR_FREE(netdev);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">char</span> *</span><br><span class="line">qemuBuildChrChardevStr(virLogManagerPtr logManager,</span><br><span class="line">            virCommandPtr cmd,</span><br><span class="line">            virQEMUDriverConfigPtr cfg,</span><br><span class="line">            <span class="keyword">const</span> virDomainDef *def,</span><br><span class="line">            <span class="keyword">const</span> virDomainChrSourceDef *dev,</span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">char</span> *alias,</span><br><span class="line">            virQEMUCapsPtr qemuCaps,</span><br><span class="line">            <span class="keyword">bool</span> nowait)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">switch</span> (dev-&gt;type) &#123;</span><br><span class="line">        <span class="keyword">case</span> VIR_DOMAIN_CHR_TYPE_UNIX:</span><br><span class="line">            virBufferAsprintf(&amp;buf, <span class="string">"socket,id=%s,path="</span>, charAlias);</span><br><span class="line">            virQEMUBuildBufferEscapeComma(&amp;buf, dev-&gt;data.nix.path);</span><br><span class="line">            <span class="comment">//此处是server的时候的添加</span></span><br><span class="line">            <span class="comment">//我们可以在这里进行reconnect的判定并且添加</span></span><br><span class="line">            <span class="keyword">if</span> (dev-&gt;data.nix.listen)</span><br><span class="line">              virBufferAdd(&amp;buf, nowait ? <span class="string">",server,nowait"</span> : <span class="string">",server"</span>, <span class="number">-1</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="之前方案"><a href="#之前方案" class="headerlink" title="之前方案"></a>之前方案</h1><hr><ul><li>qemu做client端，xml中为<code>&lt;source type=&#39;unix&#39; path=&#39;xxx&#39; mode=&#39;client&#39;/&gt;</code>。最终libvirt会将xml翻译成qemu的参数，作为命令行启动qemu。</li><li>如果不看xml，直接看qemu参数，<code>-chardev socket,id=charnet0,path=xxx</code>。</li><li>ovs做client端，创建的vhost-user端口方式为<code>ovs-vsctl add-port br-int vhostuser0 -- set Interface vhostuser0 type=dpdkvhostuser</code>。</li></ul><h1 id="方案1实施"><a href="#方案1实施" class="headerlink" title="方案1实施"></a>方案1实施</h1><hr><ul><li>让qemu做server端，此时xml中的mode需要改成server，另外就是path不能配置到/var/run/openvswitch路径中，因为ovs挂掉的时候该路径也会删除，所以会出问题。</li><li>如果不改xml，就是qemu参数，最后需要添加<code>,server</code>来告知qemu这个需要建立server端的socket连接。路径问题同上。</li><li>ovs做client端，需要将创建端口的命令改成<code>ovs-vsctl add-port br-int vhostuser0 -- set Interface vhostuser0 type=dpdkvhostuserclient options:vhost-server-path=&quot;/tmp/vhostuser0&quot;</code>，主要区别在于type和添加一个path，因为ovs做client默认是空的路径，所以必须指定，还要和上面qemu的路径一致。</li></ul><h1 id="方案2实施"><a href="#方案2实施" class="headerlink" title="方案2实施"></a>方案2实施</h1><hr><p>相对于之前的方案，只需要修改qemu的代码，让vhostuser默认支持reconnect即可，修改代码如下：<br>文件<code>qemu-char.c</code>的函数<code>qemu_chr_parse_socket</code>，将<code>int64_t reconnect = qemu_opt_get_number(opts, &quot;reconnect&quot;, 0);</code>修改为<code>int64_t reconnect = qemu_opt_get_number(opts, &quot;reconnect&quot;, 1);</code>即可。</p><blockquote><p>注意：此方案可能会对server模式有影响，只要我们确定不用qemu作为server，那就不会产生影响。</p></blockquote><h1 id="方案3实施"><a href="#方案3实施" class="headerlink" title="方案3实施"></a>方案3实施</h1><hr><p>相对于之前的方案，我们需要分别修改libvirt和qemu，让他们都支持reconnect。<br>这里需要改动的地方有一些多，晚些时候再以patch更新或者直接采用方案1或者2。</p></div><div></div><footer class="post-footer"><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/2017/02/14/VPP/2017-02-14-VPP-BiHash/" rel="next" title="[转]Cisco VPP BiHash分析"><i class="fa fa-chevron-left"></i> [转]Cisco VPP BiHash分析</a></div><div class="post-nav-prev post-nav-item"> <a href="/2017/02/21/SDN/OVN/2017-02-21-ovn-introduce/" rel="prev" title="OVN简介">OVN简介<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-copyright"><p><span>注意:</span>所有文章非特别说明皆为原创。为保证信息与源同步，转载时请务必注明文章出处！谢谢合作 :-)</p><p> <span>原始链接:</span><a class="post-url" href="/2017/02/16/QEMU/2017-02-16-qemu-reconnect/" title="QEMU vhostuser重连">http://zhaozhanxu.com/2017/02/16/QEMU/2017-02-16-qemu-reconnect/</a><span class="copy-path" data-clipboard-text="原文: http://zhaozhanxu.com/2017/02/16/QEMU/2017-02-16-qemu-reconnect/　　作者: 赵占旭" title="点击复制文章链接"><i class="fa fa-clipboard"></i></span><script src="/js/clipboard.min.js"></script><script>var clipboard=new Clipboard(".copy-path")</script></p><p> <span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/cn" title="中国大陆 (CC BY-NC-SA 4.0 CN)" target="_blank">"署名-非商用-相同方式共享 4.0"</a> 转载请保留原文链接及作者。</p></div></div></div><div class="comments" id="comments"></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> Table of Contents</li><li class="sidebar-nav-overview" data-target="site-overview"> Overview</li></ul><section class="site-overview sidebar-panel"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a href="/about"><img class="site-author-image" itemprop="image" src="/images/public/me.jpg" alt="赵占旭"><p class="site-author-name" itemprop="name">赵占旭</p><p class="site-description motion-element" itemprop="description"></p></a></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives"><span class="site-state-item-count">61</span> <span class="site-state-item-name">posts</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories"><span class="site-state-item-count">7</span> <span class="site-state-item-name">categories</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags"><span class="site-state-item-count">14</span> <span class="site-state-item-name">tags</span></a></div></nav><div class="feed-link motion-element"><a href="/atom.xml" rel="alternate"><i class="fa fa-rss"></i> RSS</a></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="mailto:zhaozhanxu@163.com" target="_blank" title="Mail"><i class="fa fa-fw fa-envelope"></i> Mail</a></span><span class="links-of-author-item"><a href="https://github.com/zhaozhanxu" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="https://www.linkedin.com/in/zhaozhanxu" target="_blank" title="LinkedIn"><i class="fa fa-fw fa-linkedin"></i> LinkedIn</a></span><span class="links-of-author-item"><a href="https://twitter.com/zhaozhanxu" target="_blank" title="Twitter"><i class="fa fa-fw fa-twitter"></i> Twitter</a></span><span class="links-of-author-item"><a href="/about/" target="_blank" title="Wechat"><i class="fa fa-fw fa-wechat"></i> Wechat</a></span><span class="links-of-author-item"><a href="/about/" target="_blank" title="QQ"><i class="fa fa-fw fa-qq"></i> QQ</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-inline"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-address-book"></i> 基情链接</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"> <a href="http://blog.csdn.net/jqh9804" title="呆子辉" target="_blank">呆子辉</a></li></ul></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#问题描述"><span class="nav-number">1.</span><span class="nav-text"><a href="#&#x95EE;&#x9898;&#x63CF;&#x8FF0;" class="headerlink" title="&#x95EE;&#x9898;&#x63CF;&#x8FF0;"></a> &#x95EE;&#x9898;&#x63CF;&#x8FF0;</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#ovs为什么不存在这个问题"><span class="nav-number">1.1.</span><span class="nav-text"><a href="#ovs&#x4E3A;&#x4EC0;&#x4E48;&#x4E0D;&#x5B58;&#x5728;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;" class="headerlink" title="ovs&#x4E3A;&#x4EC0;&#x4E48;&#x4E0D;&#x5B58;&#x5728;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;"></a> ovs&#x4E3A;&#x4EC0;&#x4E48;&#x4E0D;&#x5B58;&#x5728;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ovs-dpdk问题原因"><span class="nav-number">1.2.</span><span class="nav-text"><a href="#ovs-dpdk&#x95EE;&#x9898;&#x539F;&#x56E0;" class="headerlink" title="ovs-dpdk&#x95EE;&#x9898;&#x539F;&#x56E0;"></a> ovs-dpdk&#x95EE;&#x9898;&#x539F;&#x56E0;</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#解决方案"><span class="nav-number">2.</span><span class="nav-text"><a href="#&#x89E3;&#x51B3;&#x65B9;&#x6848;" class="headerlink" title="&#x89E3;&#x51B3;&#x65B9;&#x6848;"></a> &#x89E3;&#x51B3;&#x65B9;&#x6848;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#方案的优劣"><span class="nav-number">3.</span><span class="nav-text"><a href="#&#x65B9;&#x6848;&#x7684;&#x4F18;&#x52A3;" class="headerlink" title="&#x65B9;&#x6848;&#x7684;&#x4F18;&#x52A3;"></a> &#x65B9;&#x6848;&#x7684;&#x4F18;&#x52A3;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#qemu相关源码"><span class="nav-number">4.</span><span class="nav-text"><a href="#qemu&#x76F8;&#x5173;&#x6E90;&#x7801;" class="headerlink" title="qemu&#x76F8;&#x5173;&#x6E90;&#x7801;"></a> qemu&#x76F8;&#x5173;&#x6E90;&#x7801;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#libvirt相关源码"><span class="nav-number">5.</span><span class="nav-text"><a href="#libvirt&#x76F8;&#x5173;&#x6E90;&#x7801;" class="headerlink" title="libvirt&#x76F8;&#x5173;&#x6E90;&#x7801;"></a> libvirt&#x76F8;&#x5173;&#x6E90;&#x7801;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#之前方案"><span class="nav-number">6.</span><span class="nav-text"><a href="#&#x4E4B;&#x524D;&#x65B9;&#x6848;" class="headerlink" title="&#x4E4B;&#x524D;&#x65B9;&#x6848;"></a> &#x4E4B;&#x524D;&#x65B9;&#x6848;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#方案1实施"><span class="nav-number">7.</span><span class="nav-text"><a href="#&#x65B9;&#x6848;1&#x5B9E;&#x65BD;" class="headerlink" title="&#x65B9;&#x6848;1&#x5B9E;&#x65BD;"></a> &#x65B9;&#x6848;1&#x5B9E;&#x65BD;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#方案2实施"><span class="nav-number">8.</span><span class="nav-text"><a href="#&#x65B9;&#x6848;2&#x5B9E;&#x65BD;" class="headerlink" title="&#x65B9;&#x6848;2&#x5B9E;&#x65BD;"></a> &#x65B9;&#x6848;2&#x5B9E;&#x65BD;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#方案3实施"><span class="nav-number">9.</span><span class="nav-text"><a href="#&#x65B9;&#x6848;3&#x5B9E;&#x65BD;" class="headerlink" title="&#x65B9;&#x6848;3&#x5B9E;&#x65BD;"></a> &#x65B9;&#x6848;3&#x5B9E;&#x65BD;</span></a></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright"> &copy; 2016 - <span itemprop="copyrightYear">2017</span><span class="with-love"><i class="fa fa-heart"></i></span> <span class="author" itemprop="copyrightHolder">赵占旭</span></div><div class="powered-by"> Powered by <a class="theme-link" href="https://hexo.io">Hexo</a></div><div class="theme-info"> Theme - <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">NexT</a></div><div class="busuanzi-count"><script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="site-uv"><i class="fa fa-user"></i><span class="busuanzi-value" id="busuanzi_value_site_uv"></span></span><span class="site-pv"><i class="fa fa-eye"></i><span class="busuanzi-value" id="busuanzi_value_page_pv"></span></span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/lib/canvas-nest/canvas-nest.min.js"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script><script type="text/javascript">function proceedsearch(){$("body").append('<div class="popoverlay">').css("overflow","hidden"),$(".popup").toggle()}var isfetched=!1,search_path="search.xml";0==search_path.length&&(search_path="search.xml");var path="/"+search_path,searchFunc=function(e,t,a){"use strict";$.ajax({url:e,dataType:"xml",async:!0,success:function(e){isfetched=!0,$(".popup").detach().appendTo(".header-inner");var r=$("entry",e).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get(),c=document.getElementById(t),n=document.getElementById(a);c.addEventListener("input",function(){var e=0,t='<ul class="search-result-list">',a=this.value.trim().toLowerCase().split(/[\s\-]+/);n.innerHTML="",this.value.trim().length>1&&r.forEach(function(r){var c=!1,n=r.title.trim().toLowerCase(),s=r.content.trim().replace(/<[^>]+>/g,"").toLowerCase(),o=decodeURIComponent(r.url),i=-1,l=-1,p=-1;if(""!=n&&a.forEach(function(e,t){i=n.indexOf(e),l=s.indexOf(e),(i>=0||l>=0)&&(c=!0,0==t&&(p=l))}),c){e+=1,t+="<li><a href='"+o+"' class='search-result-title'>"+n+"</a>";var h=r.content.trim().replace(/<[^>]+>/g,"");if(p>=0){var u=p-20,d=p+80;u<0&&(u=0),0==u&&(d=50),d>h.length&&(d=h.length);var f=h.substring(u,d);a.forEach(function(e){var t=new RegExp(e,"gi");f=f.replace(t,'<b class="search-keyword">'+e+"</b>")}),t+='<p class="search-result">'+f+"...</p>"}t+="</li>"}}),t+="</ul>",0==e&&(t='<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'),""==a&&(t='<div id="no-result"><i class="fa fa-search fa-5x" /></div>'),n.innerHTML=t}),proceedsearch()}})};$(".popup-trigger").click(function(e){e.stopPropagation(),0==isfetched?searchFunc(path,"local-search-input","local-search-result"):proceedsearch()}),$(".popup-btn-close").click(function(e){$(".popup").hide(),$(".popoverlay").remove(),$("body").css("overflow","")}),$(".popup").click(function(e){e.stopPropagation()})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script></body></html>